package com.ruoyi.onlinevideo.handler;/**
 * @InterfaceName: MyWebSocketHandler
 * @Description:
 * @Author: YourName
 * @Date: 2023-04-04
 */

import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.core.constant.CacheConstants;
import com.ruoyi.common.core.constant.SecurityConstants;
import com.ruoyi.common.core.context.SecurityContextHolder;
import com.ruoyi.common.core.utils.SpringUtils;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.redis.service.RedisService;
import com.ruoyi.common.security.auth.AuthUtil;
import com.ruoyi.common.security.utils.SecurityUtils;
import com.ruoyi.onlinevideo.domain.Message;
import com.ruoyi.onlinevideo.service.MessageService;
import com.ruoyi.common.core.utils.MyMesssage;
import com.ruoyi.onlinevideo.util.WebsocketConstants;
import com.ruoyi.system.api.model.LoginUser;
import lombok.Data;
import org.springframework.amqp.core.AmqpTemplate;

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

/**
 * @InterfaceName: MyWebSocketHandler
 * @Description:
 * @Author: lilirui
 * @Date: 2023-04-04
 */
@Component
public class   MyWebSocketHandler implements WebSocketHandler {
    private static final String SIGNAL_TYPE_SYSTEM = "system";
    private static final String SIGNAL_TYPE_JOIN = "join";
    private static final String SIGNAL_TYPE_RESP_JOIN = "resp-join"; // 告知加入者对方是谁
    private static final String SIGNAL_TYPE_LEAVE = "leave";
    private static final String SIGNAL_TYPE_NEW_PEER = "new-peer";
    private static final String SIGNAL_TYPE_PEER_LEAVE = "peer-leave";
    private static final String SIGNAL_TYPE_OFFER = "offer";
    private static final String SIGNAL_TYPE_ANSWER = "answer";
    private static final String SIGNAL_TYPE_CANDIDATE = "candidate";
    private static final String SIGNAL_TYPE_CHAT = "chat";
    private static final String SIGNAL_TYPE_ONE_TO_ONE = "one_to_one";
    private static final String SIGNAL_TYPE_GET_SESSION = "getSession";
    private static final String SIGNAL_TYPE_MUT_ROOM_CHAT = "mut_room_chat";
    private static final String SIGNAL_TYPE_ERROR = "error";

    private static MessageService messageService;
    private static RedisService redisService;
    private static AmqpTemplate amqpTemplate;

    public MyWebSocketHandler() {
        redisService = (RedisService) SpringUtils.getBean(RedisService.class);
        messageService = (MessageService) SpringUtils.getBean("MessageService");
        amqpTemplate = (AmqpTemplate) SpringUtils.getBean(AmqpTemplate.class);
    }

    private final Logger logger = Logger.getLogger(String.valueOf(MyWebSocketHandler.class));

    //onlineCount：在线连接数
    private static AtomicInteger onlineCount = new AtomicInteger(0);

    //webSocketSet：用来存放每个客户端对应的MyWebSocket对象。
    public static HashMap<String, Room> roomTableMap = new HashMap<String, Room>();
    public static HashMap<String, Client> userMap = new HashMap<String, Client>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws IOException {
        HashMap<String, String> msg = new HashMap<String, String>();
        Client client = userMap.get(SecurityUtils.getLoginUser().getUserid().toString());
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (client == null) {
            client = new Client(loginUser.getUserid().toString(), loginUser.getSysUser().getNickName(), null, session);
            userMap.put(client.getUid(), client);
        } else {
            client.addSession(session);
        }
        client.setUname(loginUser.getSysUser().getNickName());
        client.setUNickName(loginUser.getSysUser().getNickName());

        msg.put("msg", "连接成功");
        msg.put("status", "SUCCESS");
        session.sendMessage(new TextMessage(JSON.toJSONString(JSON.toJSON(msg))));
    }

    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        try {
            if (message.equals("\"ping\"")) {//心跳检测
                return;
            }
            HttpHeaders handshakeHeaders = session.getHandshakeHeaders();
            String authorization = handshakeHeaders.get("authorization").get(0);
            auth(authorization);
            String string1 = message.getPayload().toString();
            MyMesssage myMesssage = new MyMesssage(string1);
            String cmd = myMesssage.getCmd();
            String user_id = session.getHandshakeHeaders().get("user_id").get(0);
            myMesssage.setUid(user_id);
            switch (cmd) {
                case SIGNAL_TYPE_JOIN:
                    join(myMesssage, session);
                    break;
                case SIGNAL_TYPE_LEAVE:
                    leave(myMesssage, session);
                    break;
                case SIGNAL_TYPE_OFFER:
                    offer(myMesssage);
                    break;
                case SIGNAL_TYPE_ANSWER:
                    answer(myMesssage);
                    break;
                case SIGNAL_TYPE_CANDIDATE:
                    candidate(myMesssage);
                    break;
                case SIGNAL_TYPE_CHAT:
                    chat(myMesssage);
                    break;
                case SIGNAL_TYPE_ONE_TO_ONE:
                    one_to_one(myMesssage);
                    break;
                case SIGNAL_TYPE_GET_SESSION:
                    getSession(myMesssage);
                    break;
                case SIGNAL_TYPE_MUT_ROOM_CHAT:
                    mut_room_chat(myMesssage);
                    break;
                case SIGNAL_TYPE_SYSTEM:
                    systemMsg(myMesssage);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RabbitListener(queues = {"mq.q1"})
    public void systemMsg(MyMesssage myMesssage) throws IOException {
        String msg = myMesssage.getMsg();
        Client client = userMap.get(myMesssage.getUid());
        if (client == null) {
            return;
        }
        client.sendMessage(new TextMessage(JSON.toJSONString(myMesssage)));
    }

    private void mut_room_chat(MyMesssage myMesssage) throws IOException {
        HashMap<String, Client> room = roomTableMap.get(myMesssage.getRoomId()).getRoomMap();
        Client sender = userMap.get(myMesssage.getUid());
        String s = JsonMsg(SIGNAL_TYPE_MUT_ROOM_CHAT, sender, myMesssage.getMsg());
        TextMessage textMessage = new TextMessage(s);
        for (String Uid : room.keySet()) {
            Client client = userMap.get(Uid);
            client.sendMessage(textMessage);
        }
    }

    private void getSession(MyMesssage myMesssage) throws IOException {
//        client client = userMap.get(myMesssage.getUid());
//        WebSocketSession session = client.getSession();
//        session.sendMessage(new TextMessage(session.toString()));
    }

    private void one_to_one(MyMesssage myMesssage) throws IOException {
        Message message = new Message();
        message.setContent(myMesssage.getMsg());
        message.setSender(Long.valueOf(myMesssage.getUid()));
        message.setReceiver(Long.valueOf(myMesssage.getRemoteUid()));
        messageService.save(message);
        Client sender = userMap.get(myMesssage.getUid());
        Client receiver = userMap.get(myMesssage.getRemoteUid());
        String s1 = JsonMsg(SIGNAL_TYPE_ONE_TO_ONE, sender, myMesssage.getMsg());
        if (receiver != null) {
            String s = s1;
            receiver.sendMessage(new TextMessage(s));
        }
        String s = s1;
        sender.sendMessage(new TextMessage(s));
        //在不在线,都存数据库
    }

    private void auth(String authorization) {
        if (StringUtils.isNotEmpty(authorization)) {
            LoginUser loginUser = AuthUtil.getLoginUser(authorization);
            if (StringUtils.isNotNull(loginUser)) {
                AuthUtil.verifyLoginUserExpire(loginUser);
                SecurityContextHolder.set(SecurityConstants.LOGIN_USER, loginUser);
            }
        }
    }

    private void ForceLeave(Client client, WebSocketSession session) throws IOException {
        MyMesssage myMesssage = new MyMesssage();
        myMesssage.setRoomId(client.getRoomId());
        myMesssage.setUid(client.getUid());
        leave(myMesssage, session);
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        System.out.println("错误处理");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws IOException {
        String user_id = session.getHandshakeHeaders().get("user_id").get(0);
        Client client = userMap.get(user_id);
        if (!Objects.isNull(client)) {
            ForceLeave(client, session);//没事就退出
        }
//        roomMap
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    public synchronized void sendMessage(WebSocketSession session, String message) throws IOException {
        TextMessage textMessage = new TextMessage(message);
        session.sendMessage(textMessage);
    }


    private void chat(MyMesssage message) throws IOException {
        mut_room_chat(message);
//        for (String userId : userIds) {
//            WebSocketSession webSocketSession = sessionMap.get(userId);
//            if (!Objects.isNull(webSocketSession)){
//                TextMessage textMessage = new TextMessage(message.getContent());
//                webSocketSession.sendMessage(textMessage);
//            }
    }

    private void candidate(MyMesssage message) throws IOException {
        offer(message);
    }

    private void answer(MyMesssage message) throws IOException {
        offer(message);
    }

    private void offer(MyMesssage message) throws IOException {
        String remoteUid = message.getRemoteUid();

        String roomId = message.getRoomId();
        HashMap<String, Client> roomMap = roomTableMap.get(roomId).getRoomMap();
        if (roomMap.containsKey(remoteUid)) {
            Client client = roomMap.get(remoteUid);
            if (Objects.isNull(client)) {
                return;
            }
            TextMessage textMessage = new TextMessage(JSON.toJSONString(message));
            client.sendMessage(textMessage);
        }
    }

    private void leave(MyMesssage message, WebSocketSession session) throws IOException {
        String uid = message.getUid();
        Client client = userMap.get(uid);
        client.getSessionList().remove(session);

        String roomId = message.getRoomId();
        Room room = roomTableMap.get(roomId);
        if (Objects.isNull(room)) {
            return;
        }
        HashMap<String, Client> roomMap = room.getRoomMap();
        if (!roomMap.containsKey(uid)) {
//            console.info("uid: " + uid + " have leave roomId " + roomId);
            return;
        }
        roomMap.remove(uid);
        if (roomMap.keySet().size() >= 1) {
            for (String memberId : roomMap.keySet()) {
                String msg = JsonMsg(SIGNAL_TYPE_PEER_LEAVE, client, "");
                Client client1 = roomMap.get(memberId);
                TextMessage textMessage = new TextMessage(msg);
                client1.sendMessage(textMessage);//通知原房间成员
            }
        }
    }

    private void join(MyMesssage message, WebSocketSession session) throws IOException {
        String uid = message.getUid();
        Client client = userMap.get(uid);

        String roomId = message.getRoomId();
        //先拿现成的
        Room room = roomTableMap.get(roomId);
        //若房间不存在，从redis找
        String key = CacheConstants.MEETING_TYPE_KEY + roomId;
        String meeting_type = redisService.getCacheObject(key);
        if (meeting_type == null) {//redis也没有，则房间非法
            client.sendMessage(new TextMessage("房间不存在"));
            return;
        }

        client.roomId = roomId;
        if (Objects.isNull(room)) {
            room = new Room();
            room.setType(meeting_type);
            room.setRoomId(roomId);
            HashMap<String, Client> roomMap = new HashMap<>();
            room.setRoomMap(roomMap);
            roomTableMap.put(roomId, room);
        }

        if (room.getType() == WebsocketConstants.DOUBLE_MEETING && room.getRoomMap().keySet().size() >= 2) {//双人会议人数不能多于2
            sendMessage(session, "roomId:" + roomId + " 已经有两人存在，请使用其他房间");
            return;
        }
        HashMap<String, Client> roomMap = room.getRoomMap();
        roomMap.put(uid, client);
        userMap.put(uid, client);
        if (roomMap.keySet().size() > 1) {//房间人数大于1时通知房间内所有人
            List<String> message1 = new ArrayList<String>();
            for (String memberId : roomMap.keySet()) {
                Client client1 = roomMap.get(memberId);
                if (!Objects.isNull(client1) && !memberId.equals(uid)) {
                    String msg = JsonMsg(SIGNAL_TYPE_NEW_PEER, client, "");
                    client1.sendMessage(new TextMessage(msg));//发给原房间成员
                    msg = JsonMsg(SIGNAL_TYPE_RESP_JOIN, client1, "");
                    client.sendMessage(new TextMessage(msg));//发给新房间成员
                }
            }
        }
    }

    public String JsonMsg(String cmd, Client client, String msg) {
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("cmd", cmd);
        stringStringHashMap.put("remoteUid", client.getUid());
        stringStringHashMap.put("remoteName", client.getUname());
        stringStringHashMap.put("remoteNickName", client.getUNickName());
        if (msg != null) {
            stringStringHashMap.put("msg", msg);
        }
        return JSON.toJSONString(stringStringHashMap);
    }


    @Data
    class Client {
        String uid;
        String uname;
        String uNickName;
        String roomId;
        Set<WebSocketSession> sessionList;

        public Client(String uid, String uname, String roomId, WebSocketSession session) {
            this.uid = uid;
            this.uname = uname;
            this.roomId = roomId;
            HashSet<WebSocketSession> webSocketSessions = new HashSet<>();
            sessionList = webSocketSessions;
            sessionList.add(session);
        }

        void sendMessage(TextMessage textMessage) throws IOException {
            for (WebSocketSession webSocketSession : sessionList) {
                if (webSocketSession != null) {
                    webSocketSession.sendMessage(textMessage);
                }
            }
        }

        public void addSession(WebSocketSession session) {
            sessionList.add(session);
        }


        public void sendError(String msg) throws IOException {
            HashMap<String, String> stringStringHashMap = new HashMap<>();
            stringStringHashMap.put("cmd", SIGNAL_TYPE_ERROR);
            stringStringHashMap.put("msg", msg);
            if (msg != null) {
                stringStringHashMap.put("msg", msg);
            }
            sendMessage(new TextMessage(JSON.toJSONString(stringStringHashMap)));
        }
    }

    @Data
    class Room {
        String roomId;
        String type;//"double"双人会议，"multiple"多人会议，"predetermine"预定
        HashMap<String, Client> roomMap;
    }
}
